namespace Radar.Test
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class ContainerTest
    {
        private interface I1 { }
        private class A1 : I1 { }

        private interface I2<T> { }
        private class A2<T> : I2<T> { }

        [Test]
        public void WillRegisterAndResolveTransient()
        {
            var container = new Container();
            container.Register<I1, A1>().WithLifetime(Lifetime.Transient);

            var x1 = container.Resolve<I1>();
            var x2 = container.Resolve<I1>();

            Assert.That(x1, Is.Not.EqualTo(x2));
        }

        [Test]
        public void WillRegisterAndResolveSingleton()
        {
            var container = new Container();
            container.Register<I1, A1>().WithLifetime(Lifetime.Singleton);

            var x1 = container.Resolve<I1>();
            var x2 = container.Resolve<I1>();

            Assert.That(x1, Is.EqualTo(x2));
        }

        [Test]
        public void WillRegisterByDefaultWithTypeName()
        {
            var container = new Container();
            container.Register<I1, A1>();

            var x = container.Resolve(typeof(I1).AssemblyQualifiedName);

            Assert.That(x, Is.InstanceOf<A1>());
        }

        [Test]
        public void WillResolveByDefaultWithTypeName()
        {
            var container = new Container();
            container.Register<I1, A1>(typeof(I1).AssemblyQualifiedName);

            var x = container.Resolve<I1>();

            Assert.That(x, Is.InstanceOf<A1>());
        }

        [Test]
        public void WillReturnIfTypeIsRegistered()
        {
            var container = new Container();
            var result1 = container.IsRegistered<I1>();
            Assert.That(result1, Is.False);

            container.Register<I1, A1>();

            var result2 = container.IsRegistered<I1>();
            Assert.That(result2, Is.True);
        }

        [Test]
        public void WillResolveWithServiceType()
        {
            var container = new Container();
            container.Register(typeof(I1), typeof(A1));

            var result = container.Resolve(typeof(I1));
            Assert.That(result, Is.InstanceOf<A1>());
        }

        [Test]
        public void WillRegisterTypeWithKey()
        {
            var container = new Container();
            container.Register(typeof(I1), typeof(A1), "key");

            Assert.That(container.IsRegistered("key"), Is.True);
        }

        [Test]
        public void WillRegisterWithInstanceBuilder()
        {
            var container = new Container();
            var a = new A1();
            Func<object> builder = () => a;

            container.Register(typeof(I1), builder);

            Assert.That(container.IsRegistered<I1>(), Is.True);
        }

        [Test, ExpectedException(typeof(IocConfigurationException))]
        public void WillThrowOnInvalidTypes()
        {
            var container = new Container();
            container.Register(typeof(I1), typeof(object));
        }

        [Test, ExpectedException(typeof(DuplicateImplementationIoCException))]
        public void WillThrowOnSameKey()
        {
            var container = new Container();
            container.Register<I1, A1>();
            container.Register<I1, A1>();
        }

        [Test, ExpectedException(typeof(ServiceNotRegisteredException))]
        public void WillThrowOnUnregisteredService()
        {
            var container = new Container();
            container.Resolve<I1>();
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void WillThrowOnRegisterWillNullKey()
        {
            var container = new Container();
            container.Register<I1, A1>(string.Empty);
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void WillThrowOnResolveWithNullKey()
        {
            var container = new Container();
            container.Resolve(string.Empty);
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void WillThrowOnResolveWithNullType()
        {
            var container = new Container();
            container.Resolve((Type)null);
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void WillThrowOnResolveWithNullContext()
        {
            var container = new Container();
            container.Resolve<int>(null);
        }

        [Test, ExpectedException(typeof(DuplicateImplementationIoCException))]
        public void WillThrowOnDuplicateRegistrationWithFactory()
        {
            var container = new Container();
            Func<object> builder = () => null;
            container.Register(typeof(I1), builder);
            container.Register(typeof(I1), builder);
        }

        [Test]
        public void WillRegisterAutomaticallySimpleType()
        {
            var container = new Container(true, null);

            var result = container.Resolve<I1>();

            Assert.That(result, Is.InstanceOf<A1>());
        }

        [Test, ExpectedException(typeof(TypeResolveException))]
        public void WillNotRegisterGenericType()
        {
            var container = new Container(true, null);

            container.Resolve<I2<int>>();
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void WillThrowOnNullServiceTypeRegistration()
        {
            var container = new Container();
            container.Register(null, typeof(A1), "some key");
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void WillThrowOnNullImplementationRegistration()
        {
            var container = new Container();
            container.Register(typeof(I1), null, "some key");
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void WillThrowOnNullServiceWhileCheckingRegistration()
        {
            var container = new Container();
            container.IsRegistered((Type)null);
        }

        [Test]
        public void WillUseCallbackAction()
        {
            string key = null;
            IIocConfiguration configuration = null;

            Action<string, IIocConfiguration> callback = (k, c) =>
                                                             {
                                                                 key = k;
                                                                 configuration = c;
                                                             };

            var container = new Container(true, callback);

            var result = container.Resolve<I1>();

            Assert.That(result, Is.InstanceOf<A1>());
            Assert.That(key, Is.EqualTo(typeof(I1).AssemblyQualifiedName));
            Assert.That(configuration, Is.Not.Null);
        }
    }
}